package com.young.common.core.excel;

import com.young.common.util.*;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * Excel导出类
 * Created by rookie on 2017/11/3.
 */
public class ExcelExport implements IExcelExport {

    private static final Logger logger = LoggerFactory.getLogger(ExcelExport.class);
    private Workbook workbook;//工作簿
    private Sheet[] sheets;//工作表数组,对应excel表格中的多个sheet页
    private Sheet coreSheet;//核心sheet,记录导出数据的sheet页
    private int size;//数据量,含标题行
    private boolean hasHeader;//标题行标志位

    public ExcelExport(){
        workbook = new HSSFWorkbook();
        coreSheet = workbook.createSheet();//创建核心sheet
    }

    @Override
    public void insertHead(String[] headers) {
        if (!hasHeader){//当标题行不存在时才插入标题行
            Row row = coreSheet.createRow(0);
            for (short i = 0; i < headers.length; i++) {
                Cell cell = row.createCell(i);
                RichTextString text = new HSSFRichTextString(headers[i]);
                cell.setCellValue(text);
            }
            size++;
            hasHeader = true;
        }else {
            logger.warn("[Excel导出-写入标题行] 已存在标题行,不允许进行第二次标题行插入,参数为:{}", headers);
        }
    }

    @Override
    public int insertRow(List<Object> data) {
        Row row = coreSheet.createRow(size);
        for (int i = 0; i < data.size(); i++) {
            Object obj = data.get(i);
            Cell cell = row.createCell(i);
            if (obj instanceof String) {
                cell.setCellValue((String) obj);
            } else if (obj instanceof Integer) {
                cell.setCellValue((Integer) obj);
            } else if (obj instanceof Long) {
                cell.setCellValue((Long) obj);
            } else if (obj instanceof Double) {
                cell.setCellValue((Double) obj);
            } else if (obj instanceof Float) {
                cell.setCellValue((Float) obj);
            } else if (obj instanceof Date) {
                //DataFormat format = workbook.createDataFormat();
                //cell.setCellValue((Date) obj);
                cell.setCellValue(com.young.common.util.DateUtil.dateFormat(obj));
            } else{
                cell.setCellValue(obj != null ? obj.toString() : "");
            }
        }
        size++;
        return 1;
    }

    @Override
    public int insertRow(Object[] data) {
        return insertRow(Arrays.asList(data));
    }

    @Override
    public int insertRows(List<List<Object>> list) {
        int count = 0;
        for (int j = 0;j<list.size();j++){
            List<Object> obj = list.get(j);
            insertRow(obj);
            count++;
        }
        return count;
    }

    @Override
    public <T> void insertBeanList(List<T> list, Class<T> cls) throws NoSuchMethodException {
        //读取cls中的ExcelColumn注解信息
        List<ExcelColumnUnit> unitList = new ArrayList<ExcelColumnUnit>();
        Field[] fields = cls.getDeclaredFields();//所有字段
        for (Field field : fields){
            ExcelColumnUnit excelColumnUnit = new ExcelColumnUnit();
            ExcelColumn excelColumn = field.getAnnotation(ExcelColumn.class);
            if (excelColumn != null){
                excelColumnUnit.setField(field);//字段
                excelColumnUnit.setExcelColumn(excelColumn);//注解
                //字段set方法
                try {
                    excelColumnUnit.setGetMethod(cls.getMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1), null));
                } catch (NoSuchMethodException e) {
                    logger.error("[Excel导出] 字段{}没有对应的public get方法", field.getName());
                    e.printStackTrace();
                    throw e;
                }
                unitList.add(excelColumnUnit);
            }
        }

        //对单元进行排序
        Collections.sort(unitList, new Comparator<ExcelColumnUnit>() {
            @Override
            public int compare(ExcelColumnUnit o1, ExcelColumnUnit o2) {
                if (o1.getExcelColumn().order() < o2.getExcelColumn().order()){
                    return -1;
                }else if (o1.getExcelColumn().order() > o2.getExcelColumn().order()){
                    return 1;
                }
                return 0;
            }
        });

        //当标题行不存在时插入标题行
        if (!hasHeader){
            String[] headers = new String[unitList.size()];//标题行
            int i = 0;
            for (ExcelColumnUnit unit : unitList){
                headers[i++] = unit.getExcelColumn().value();
            }
            this.insertHead(headers);
        }

        //写入workbook
        for (T item : list){
            List<Object> row = new ArrayList<Object>(unitList.size());
            for (ExcelColumnUnit unit : unitList){
                try {
                    row.add(unit.getGetMethod().invoke(item, null));
                } catch (IllegalAccessException e) {
                    logger.error("[Excel导出] 方法{}执行异常", unit.getSetMethod().getName());
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    logger.error("[Excel导出] 方法{}执行异常", unit.getSetMethod().getName());
                    e.printStackTrace();
                }
            }
            this.insertRow(row);
        }
    }

    @Override
    public void write2Response(HttpServletResponse response, String fileName) {
        this.write2Response(null, response, fileName);
    }

    @Override
    public void write2Response(HttpServletRequest request, HttpServletResponse response, String fileName) {
        //写入流
        response.setContentType("application/x-msdownload;");//文件下载类型
        OutputStream os = null;
        try {
            if (request != null){//如果request有效,则使用工具类提供的兼容ie的方式
                FileDownloadUtil.setFileNameOfCN(request, response, fileName);
            }else{
                response.setHeader("Content-disposition", "attachment; filename="
                        + new String( fileName.getBytes("utf-8"), "ISO8859-1" ));//设置文件名,转码是为了解决中文乱码问题
            }
            os = response.getOutputStream();
            workbook.write(os);//写到输出流
        }catch (IOException e) {
            logger.error("[Excel导出-写入响应] 发生异常!!!文件名为: {}", fileName);
            e.printStackTrace();
        }finally {
            try {
                if(os != null){
                    os.flush();
                    os.close();
                }
            } catch (IOException e) {
                logger.error("[Excel导出-写入响应] *异常* 输出流关闭时发生异常!!!");
                e.printStackTrace();
            }
        }
    }

    /**
     * 写入流
     *
     * @param outputStream
     */
    @Override
    public void write2Stream(OutputStream outputStream) throws IOException {
        workbook.write(outputStream);//写到输出流
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public int getSheetNum() {
        return sheets != null ? sheets.length : 0;
    }

    public Workbook getWorkbook() {
        return workbook;
    }

    public void setWorkbook(Workbook workbook) {
        this.workbook = workbook;
    }

    public Sheet[] getSheets() {
        return sheets;
    }

    public void setSheets(Sheet[] sheets) {
        this.sheets = sheets;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public boolean isHasHeader() {
        return hasHeader;
    }

    public void setHasHeader(boolean hasHeader) {
        this.hasHeader = hasHeader;
    }

    public Sheet getCoreSheet() {
        return coreSheet;
    }

    public void setCoreSheet(Sheet coreSheet) {
        this.coreSheet = coreSheet;
    }
}
